﻿/* 弹出菜单控件
 */

/* 待重构
 * 待测试*/
namespace LSLib.Windows.Forms.ControlEx
{
    using System;
    using System.Drawing;
    using System.Windows.Forms;

    public partial class Menu : Control
    {
        public event EventHandler MenuClosed;
        public delegate Size DrawMenu(Control canvas);
        public delegate Rectangle CalcMenuBounds(Control owner, Point location, Size size, out bool scrollNeeded);

        private Control m_Owner;

        private Menu(Control owner)
        {
            this.m_Owner = owner;
            this.m_Owner.ParentChanged += new EventHandler(owner_ParentChanged);
        }

        public static Menu Open(Control owner, Point location, DrawMenu drawCallback)
        {
            return Menu.Open(owner, location, drawCallback, null);
        }
        public static Menu Open(Control owner, Point location, DrawMenu drawCallback, CalcMenuBounds boundsCallback)
        {
            Menu menu = new Menu(owner);
            Panel canvas = new Panel();
            Size size = drawCallback(canvas);
            canvas.Parent = menu;
            canvas.Bounds = new Rectangle(new Point(), size);
            //if (boundsCallback == null) boundsCallback = Menu.CalcBounds;
            bool scrollNeeded;
            Rectangle workBounds = Menu.CalcBounds(owner, location, size, out scrollNeeded);
            menu.Bounds = workBounds;
            if (scrollNeeded) NeedVScrollBar(menu, canvas, workBounds.Height, size.Height);
            menu.Parent = owner.FindForm();
            menu.RegisterClickEvents(menu.Parent);
            menu.BringToFront();
            menu.Show();
            return menu;
        }
        private static void NeedVScrollBar(Control parent, Panel tag, int minimum, int maximum)
        {
            VScrollBar scroll = new VScrollBar();
            scroll.Parent = parent;
            scroll.Tag = tag;
            scroll.Minimum = minimum;
            scroll.Maximum = maximum;
            scroll.Dock = DockStyle.Right;
            scroll.ValueChanged += new EventHandler(scroll_ValueChanged);
            scroll.BringToFront();
            scroll.Show();
        }
        private static void scroll_ValueChanged(object sender, EventArgs e)
        {
            VScrollBar scroll = sender as VScrollBar;
            Panel canvas = scroll.Tag as Panel;
            canvas.Top = scroll.Minimum - scroll.Value;
        }

        private static Rectangle CalcBounds(Control owner, Point location, Size size, out bool scrollNeeded)
        {
            Control root = owner;
            while (root.Parent != null)
            {
                location.X += root.Left;
                location.Y += root.Top;
                root = root.Parent;
            }
            if (root.ClientSize.Height - location.Y >= size.Height)
            {
                scrollNeeded = false;
                return new Rectangle(location, size);
            }
            else
            {
                if (location.Y >= size.Height)
                {
                    scrollNeeded = false;
                    return new Rectangle(new Point(location.X, location.Y - size.Height), size);
                }
                else
                {
                    scrollNeeded = true;
                    if (size.Height - (root.ClientSize.Height - location.Y) < size.Height - location.Y)
                    {
                        return new Rectangle(location, new Size(size.Width - 24, root.ClientSize.Height - location.Y));
                    }
                    else
                    {
                        return new Rectangle(new Point(location.X, 0), new Size(size.Width - 24, location.Y));
                    }
                }
            }
        }

        protected override void Dispose(bool disposing)
        {
            MenuClosed = null;
            this.Close();
            this.m_Owner.ParentChanged -= new EventHandler(owner_ParentChanged);
            base.Dispose(disposing);
        }

        public void Close()
        {
            this.OnMenuClosed(new EventArgs());
            this.UnregisterClickEvents(this.Parent);
            this.m_Owner.ParentChanged -= new EventHandler(owner_ParentChanged);
            this.Parent = null;
            this.Hide();
        }

        protected virtual void OnMenuClosed(EventArgs e)
        {
            if (MenuClosed != null) MenuClosed(this, e);
        }
        private void owner_ParentChanged(object sender, EventArgs e)
        {
            this.UnregisterClickEvents(this.Parent);
            this.Parent = m_Owner.FindForm();
            this.RegisterClickEvents(this.Parent);
        }

        private void RegisterClickEvents(Control c)
        {
            if (c == null) return;
            c.MouseDown += new MouseEventHandler(c_MouseDown);
            foreach (Control cc in c.Controls)
            {
                this.RegisterClickEvents(cc);
            }
        }

        private void UnregisterClickEvents(Control c)
        {
            if (c == null) return;
            c.MouseDown -= new MouseEventHandler(c_MouseDown);
            foreach (Control cc in c.Controls)
            {
                this.UnregisterClickEvents(cc);
            }
        }

        private void c_MouseDown(object sender, MouseEventArgs e)
        {
            Control c = sender as Control;
            bool flag = false;
            while (c != null)
            {
                if (c == this)
                {
                    flag = true;
                    break;
                }
                c = c.Parent;
            }
            if (!flag)
            {
                this.Close();
                this.Dispose();
            }
        }
    }
}
